import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Graph {
	
	private Graph instance = null;
	private int[][] adjacentMatrix;
	private String[] nodeContents;
	List<Node> nodes = new ArrayList<Node>();
	Node startNode = new Node();
	final int size = 2000;					// size of the search space, i.e nodes count
	
	public Graph(){
		this.adjacentMatrix = generateMatrix(size, 1000);
		this.nodeContents = createNodeContentsArray(size);
		this.nodes = this.setNodes(nodeContents);
//		this.adjacentMatrix = this.createMatrix(towns, costs, neighbors);
//		this.nodeContents = this.towns;
//		this.nodes = this.setNodes(nodeContents);
	}
	
	public Graph(Graph graph){
		this.adjacentMatrix = graph.adjacentMatrix;
		this.nodeContents = graph.nodeContents;
		this.nodes = this.setNodes(graph.getNodeContents());
	}

	public Graph getInstance(){
		if(instance == null){
			instance = new Graph();
		}
		return instance;
	}

	public Node getFirstNode(String first){
		Node node = null;
		for(int i = 0; i < this.nodeContents.length; i++){
			if(this.nodeContents[i].equals(first)){
				node = nodes.get(i);
			}
		}
		return node;
	}

	public Node getNodeByName(String name){
		Node node = null;
		for(int i = 0; i < nodeContents.length; i++){
			if(nodeContents[i].equals(name)){
				node = nodes.get(i);
				return node;
			}
		}
		return node;
	}
	
	public List<Node> getChildren(Node node){
		int i = nodes.indexOf(node);
		List<Node> children = new ArrayList<Node>();
		for(int j = 0; j < this.adjacentMatrix.length; j++){
			Node child = this.nodes.get(j);
			if(this.adjacentMatrix[i][j] != 0 && !child.isVisited()){
//				neighbor.setParent(node);
//				neighbor.setCost(this.adjacentMatrix[i][j]);
				children.add(child);
			}
		}
		
		return children;
	}
	
	public List<Node> getNeighbours(Node node){
		int i = nodes.indexOf(node);
		List<Node> neighbours = new ArrayList<Node>();
		for(int j = 0; j < this.adjacentMatrix.length; j++){
			Node neighbor = this.nodes.get(j);
			if(this.adjacentMatrix[i][j] != 0 && !neighbor.isVisited()){
				neighbor.setParent(node);
				neighbor.setCost(this.adjacentMatrix[i][j]);
				neighbours.add(neighbor);
			}
		}
		
		return neighbours;
	}
	
	public int getInitCost(Node node) {
//		System.out.println(node.getContent());
		int i = nodes.indexOf(node);
		int cost = 0;
		
		if (node.getParent() != null) {
			int j = nodes.indexOf(node.getParent());
			cost = this.adjacentMatrix[j][i];
		}
		
		return cost;
	}
	public void addNode(Node node){
		this.nodes.add(node);
	}
	
	public int[][] createMatrix(String[] towns, int[][] costs, int[][] neighbors){
		int size = towns.length;
		this.adjacentMatrix = new int[size][size];
		for(int i = 0; i < size; i++){
			int[] neighbor = neighbors[i];
			int[] cost = costs[i];
			for(int j = 0; j < neighbor.length; j++){
				this.adjacentMatrix[i][neighbor[j]-1] = cost[j];	
			}

		}
		
		return this.adjacentMatrix;
	}
	
	public String[] createNodeContentsArray(int size){
		this.nodeContents = new String[size];
		
		for(int i=0; i < size; i++){
//			this.nodeContents[i] = Character.toString(c);
			this.nodeContents[i] = (i+1) + "";
		}
		
		return this.nodeContents;
	}
	
	public int[][] generateMatrix(int size, int maxValue){
		// naabrusmaatriks
		int[][] adjacentMatrix = new int[size][size];
		Random random = new Random();
		for(int i = 0; i < size; i++){
			for(int j = 0; j < size; j++){

				if(i == j){
					adjacentMatrix[i][j] = 0;
				}

				if(i < j){
					int randInt = random.nextInt(maxValue) + 5;   // adding minValue as 5
					adjacentMatrix[i][j] = randInt;
					adjacentMatrix[j][i] = adjacentMatrix[i][j];
				}
			}

			}

			return adjacentMatrix;
	}
	
	public int[][] getAdjacentMatrix() {
		return adjacentMatrix;
	}
	
	public void setAdjacentMatrix(int[][] adjacentMatrix) {
		this.adjacentMatrix = adjacentMatrix;
	}
	
	public List<Node> getNodes() {
		return nodes;
	}
	
	public void setNodes(List<Node> nodes) {
		this.nodes = nodes;
	}
	
	public List<Node> setNodes(String[] nodeNames){
		List<Node> nodes = new ArrayList<Node>();
		for(String name : nodeNames){
			nodes.add(new Node(name));
		}
		
		return nodes;
	}
	
	public Node getStartNode() {
		return startNode;
	}

	public void setStartNode(Node startNode) {
		this.startNode = startNode;
	}
	
	public String[] getNodeContents() {
		return nodeContents;
	}

	public void setNodeContents(String[] nodeContents) {
		this.nodeContents = nodeContents;
	}

	String[] towns = {
			"Arad", "Zerind", "Oradea", "Sibiu", "Timisoara", 
			"Lugoj", "Mehadia",	"Drobeta", "Rimnicu Vilcea", "Craiova",
			"Pitesti", "Fagaras","Bucharest", "Giurgiu","Urziceni",
			"Neamt", "Iasi", "Vaslui", "Hirsova", "Eforie"
	};
	
	int[][] neighbors = {
			{2, 4, 5},
			{1, 3},
			{2, 4},
			{1, 3, 9, 12},
			{1, 6},
			{5, 7},
			{6, 8},
			{7, 10},
			{4, 10, 11},
			{8, 9, 11}, 
			{9, 10, 13},
			{4, 13},
			{12, 11, 14, 15},
			{13},
			{13, 18, 19},
			{17},
			{16, 18},
			{17, 15},
			{15, 20},
			{19}
	};
	
	int[][] costs = {
			{75, 140,118},
			{75, 71},
			{71, 151},
			{140, 151, 80, 99},
			{118, 111},
			{111, 70},
			{70, 75},
			{75, 120},
			{60, 146, 99, 97},
			{120, 146, 138},
			{97, 138, 101},
			{99, 211},
			{211, 101, 90, 85},
			{90},
			{85, 142, 98},
			{87},
			{87, 92},
			{92, 142},
			{98, 86},
			{86}
	};	
}
